#include <stdio.h>
#include <stdlib.h>
#include <setjmp.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include "jpeglib.h"

// 自定义错误管理器，用于捕获解码过程中的错误
struct my_error_mgr
{
    struct jpeg_error_mgr pub; // libjpeg的公共错误结构体
    jmp_buf setjmp_buffer;     // 错误跳转缓冲区
};
typedef struct my_error_mgr *my_error_ptr;

// 错误处理回调函数，替代默认的exit行为
METHODDEF(void)
my_error_exit(j_common_ptr cinfo)
{
    // 将错误指针转换为自定义错误管理器类型
    my_error_ptr myerr = (my_error_ptr)cinfo->err;

    // 打印libjpeg原生错误信息
    (*cinfo->err->output_message)(cinfo);

    // 跳回setjmp设置的位置，携带错误标记
    longjmp(myerr->setjmp_buffer, 1);
}

/**
 * @brief 解码JPEG文件为RGB数据
 * @param filename 输入JPEG文件路径
 * @param width 输出图片宽度（指针，用于返回实际宽度）
 * @param height 输出图片高度（指针，用于返回实际高度）
 * @return 分配的RGB数据缓冲区（每个像素3字节：R、G、B），失败返回NULL
 */
unsigned char *decode_jpeg(const char *filename, int *width, int *height)
{
    // 初始化JPEG解码结构体
    struct jpeg_decompress_struct cinfo;
    struct my_error_mgr jerr;

    FILE *infile;            // 输入文件指针
    JSAMPARRAY buffer;       // 一行像素数据的缓冲区
    int row_stride;          // 一行像素的字节数
    unsigned char *rgb_data; // 存储最终的RGB数据
    unsigned char *ptr;      // 用于填充RGB数据的指针

    // 打开JPEG文件
    if ((infile = fopen(filename, "rb")) == NULL)
    {
        fprintf(stderr, "无法打开文件: %s\n", filename);
        return NULL;
    }

    // 配置错误处理
    cinfo.err = jpeg_std_error(&jerr.pub);
    jerr.pub.error_exit = my_error_exit;

    // 设置错误跳转点：如果解码过程出错，会跳回这里
    if (setjmp(jerr.setjmp_buffer))
    {
        jpeg_destroy_decompress(&cinfo);
        fclose(infile);
        fprintf(stderr, "JPEG解码失败\n");
        return NULL;
    }

    // 初始化JPEG解码器
    jpeg_create_decompress(&cinfo);

    // 指定输入文件
    jpeg_stdio_src(&cinfo, infile);

    // 读取JPEG文件头，获取图片信息
    jpeg_read_header(&cinfo, TRUE);

    // 开始解码
    jpeg_start_decompress(&cinfo);

    // 获取图片宽度和高度
    *width = cinfo.output_width;
    *height = cinfo.output_height;

    // 计算一行像素的字节数（宽度 × 3通道）
    row_stride = cinfo.output_width * cinfo.output_components;

    // 分配存储RGB数据的缓冲区
    rgb_data = (unsigned char *)malloc(*width * *height * 3);
    if (rgb_data == NULL)
    {
        fprintf(stderr, "内存分配失败\n");
        jpeg_destroy_decompress(&cinfo);
        fclose(infile);
        return NULL;
    }
    ptr = rgb_data;

    // 分配一行像素的缓冲区
    buffer = (*cinfo.mem->alloc_sarray)((j_common_ptr)&cinfo, JPOOL_IMAGE, row_stride, 1);

    // 逐行读取解码后的数据并存储到RGB缓冲区
    while (cinfo.output_scanline < cinfo.output_height)
    {
        // 读取一行像素数据
        jpeg_read_scanlines(&cinfo, buffer, 1);

        // 将一行数据复制到RGB缓冲区
        memcpy(ptr, buffer[0], row_stride);
        ptr += row_stride;
    }

    // 完成解码
    jpeg_finish_decompress(&cinfo);

    // 释放解码器资源
    jpeg_destroy_decompress(&cinfo);

    // 关闭文件
    fclose(infile);

    printf("JPEG解码成功: %s\n", filename);
    printf("图片尺寸: %dx%d 像素\n", *width, *height);
    return rgb_data;
}

int main()
{
    // 测试：解码示例图片
    int width, height;
    // unsigned char *rgb_data = decode_jpeg("red_image.jpg", &width, &height);
    unsigned char *rgb_data = decode_jpeg("gradient_image.jpg", &width, &height);
    if (rgb_data == NULL)
    {
        return -1;
    }

    // 将RGB数据保存为原始文件（可用于验证解码结果）
    FILE *fp = fopen("test.rgb", "wb");
    if (fp != NULL)
    {
        fwrite(rgb_data, 1, width * height * 3, fp);
        fclose(fp);
        printf("RGB原始数据已保存至: %s\n", "test.rgb");
    }
    printf("RGB数据已保存至: %s\n", "test.rgb");

    // 测试开发板显示

    int fd_lcd;
    fd_lcd = open("/dev/fb0", O_RDWR);

    if (fd_lcd < 0)
    {
        printf("open lcd error: %s\n", strerror(errno));
        return -1;
    }
    printf("open lcd success\n");

    int (*lcd_mmap)[800] = mmap(NULL, 800 * 480 * 4, PROT_READ | PROT_WRITE, MAP_SHARED, fd_lcd, 0);

    for (int i = 0; i < height; i++)
    {
        for (int j = 0; j < width; j++)
        {
            // 计算当前像素在 RGB 数据中的起始地址
            unsigned char *pixel = rgb_data + (i * width + j) * 3;

            // 提取 R、G、B 分量（注意：RGB 数据是 unsigned char 类型）
            unsigned char r = pixel[0]; // 红色分量
            unsigned char g = pixel[1]; // 绿色分量
            unsigned char b = pixel[2]; // 蓝色分量

            // 转换为 LCD 所需的 ARGB 格式（或 ARGB 中的 RGB，alpha 设为 0）
            // 假设 LCD 每个像素为 32 位（0x00RRGGBB，高 8 位为 alpha 透明通道）
            unsigned int color = (r << 16) | (g << 8) | (b << 0);

            // 写入帧缓冲区
            lcd_mmap[i][j] = color;
        }
    }
    munmap(lcd_mmap, 800 * 480 * 4);
    close(fd_lcd);

    // 释放RGB数据缓冲区
    free(rgb_data);

    return 0;
}